/*
 * Copyright (c) 2011
 * UNPUBLISHED PROPRIETARY SOURCE CODE.
 */

package com.webservice.service.impl;

import com.webservice.domain.User;
import com.webservice.domain.dao.UserDao;
import com.webservice.domain.dao.UserPhotoDao;
import com.webservice.exception.EmailAlreadyInUseException;
import com.webservice.exception.NicknameAlreadyInUseException;
import com.webservice.exception.TokenAlreadyInUseException;
import com.webservice.exception.user.InvalidPasswordException;
import com.webservice.exception.user.SignInNotFoundException;
import com.webservice.exception.user.UserNotFoundException;
import com.webservice.service.IUserService;
import com.webservice.utils.EmailUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.inject.Inject;

/**
 * {@link User} management service.
 *
 * @author vkolodrevskiy
 */
@Service
public class UserService implements IUserService {
    private static final Logger logger = LoggerFactory.getLogger(UserService.class);

    private final UserDao userDao;
    private final UserPhotoDao userPhotoDao;
    private final PasswordEncoder passwordEncoder;

    @Inject
    public UserService(UserDao userDao, UserPhotoDao userPhotoDao, PasswordEncoder passwordEncoder) {
        this.userDao = userDao;
        this.userPhotoDao = userPhotoDao;
        this.passwordEncoder = passwordEncoder;
    }

    /**
     * {@inheritDoc}
     * @see com.webservice.service.IUserService#createUser(com.webservice.domain.User)
     */
    @Override
    @Transactional
    public User createUser(User user) throws NicknameAlreadyInUseException, EmailAlreadyInUseException, TokenAlreadyInUseException {
        if(userDao.findByNickname(user.getNickname()) != null) {
            logger.debug("Nickname = [" + user.getNickname() + "]" + " is already used.");
            throw new NicknameAlreadyInUseException(user.getNickname());
        }

        if(userDao.findByEmail(user.getEmail()) != null) {
            logger.debug("Email = [" + user.getEmail() + "]" + " is already used.");
            throw new EmailAlreadyInUseException(user.getEmail());
        }

        if(userDao.findByToken(user.getToken()) != null) {
            logger.debug("Token = [" + user.getToken() + "]" + " is already used.");
            throw new TokenAlreadyInUseException(user.getEmail());
        }

        user.setPassword(passwordEncoder.encode(user.getPassword()));

        userDao.insert(user);

        logger.info("Created user user with name = " + user.getNickname());
        return userDao.findById(user.getId());
    }

    @Override
    @Transactional
    public User updateUser(User user) throws NicknameAlreadyInUseException, EmailAlreadyInUseException, UserNotFoundException {
        if(StringUtils.isEmpty(user.getToken()))
            throw new UserNotFoundException("User not found. Token = [" + user.getToken() + "]");

        User u1 = userDao.findByToken(user.getToken());
        if(u1 == null) {
            logger.debug("User not found. Token = [" + user.getToken() + "]");
            throw new UserNotFoundException("User not found. Token = [" + user.getToken() + "]");
        }

        User u2 = userDao.findByNickname(user.getNickname());
        if(u2 != null && u2.getId() != u1.getId()) {
            logger.debug("Nickname = [" + user.getNickname() + "]" + " is already used.");
            throw new NicknameAlreadyInUseException(user.getNickname());
        }

        User u3 = userDao.findByEmail(user.getEmail());
        if(u3 != null && !u3.getEmail().equals(u1.getEmail())) {
            logger.debug("Email = [" + user.getEmail() + "]" + " is already used.");
            throw new EmailAlreadyInUseException(user.getEmail());
        }

        // do not check whether token is used or not coz its value is setup only once when creating user

        user.setPassword(passwordEncoder.encode(user.getPassword()));

        userDao.update(user);

        logger.info("Updated user user with Token = " + user.getToken());
        return userDao.findByToken(user.getToken());
    }

    @Override
    public User login(String signin, String password) throws SignInNotFoundException, InvalidPasswordException {
        User user;
        if(EmailUtils.isEmail(signin)) {
            user = userDao.findByEmail(signin);
        } else {
            user = userDao.findByNickname(signin);
        }

        if(user == null)
            throw new SignInNotFoundException(signin + " was not found.");
        else {
            if(!passwordEncoder.matches(password, user.getPassword()))
                throw new InvalidPasswordException("invalid password = [" + password + "] for signin = [" + signin + "]");
        }
        return user;
    }

    @Override
    public User findUserById(long id) throws UserNotFoundException {
        User user = userDao.findById(id);
        if(user == null) {
            throw new UserNotFoundException("User with id = [" + id + "] was not found.");
        }

        logger.info("Found user user with id = [" + id + "]");
        return user;
    }

    @Override
    public User findUserByToken(String token) throws UserNotFoundException {
        User user = userDao.findByToken(token);
        if(user == null) {
            throw new UserNotFoundException("User with token = [" + token + "] was not found.");
        }

        logger.info("Found user user with token = [" + token + "]");
        return user;
    }

    @Override
    public void removeUserByToken(String token) {
        try {
            User u = findUserByToken(token);
            userDao.removeById(u.getId());

            // remove user photo also
            userPhotoDao.removeByUserId(u.getId());
        } catch (UserNotFoundException e) {
            logger.error("Error while removing user.", e);
        }

        logger.info("Removed(set active=false) user with token = [" + token + "]");
    }
}
